package redis

import (
	"context"
	"fmt"
	"github.com/go-redis/redis/v8"
	"time"
)

type RConfig struct {
	MinIdle            int // 在启动阶段创建指定数量的Idle连接，并长期维持idle状态的连接数不少于指定数量；。
	MaxActive          int // 连接池最大socket连接数，默认为4倍CPU数， 4 * runtime.NumCPU
	DialTimeout        int // 连接建立超时时间，默认5秒。
	ReadTimeout        int // 读超时，默认3秒， -1表示取消读超时
	WriteTimeout       int // 写超时，默认等于读超时
	PoolTimeout        int // 当所有连接都处在繁忙状态时，客户端等待可用连接的最大等待时长，默认为读超时+1秒。
	IdleTimeout        int // 闲置超时，默认5分钟，-1表示取消闲置超时检查
	IdleCheckFrequency int // 闲置连接检查的周期，默认为1分钟，-1表示不做周期性检查，只在客户端获取连接时对闲置连接进行处理。
	MaxConnAge         int // 连接存活时长，从创建开始计时，超过指定时长则关闭连接，默认为0，即不关闭存活时长较长的连接
	MaxRetries         int // 命令执行失败时，最多重试多少次，默认为0即不重试
	MinRetryBackoff    int // 每次计算重试间隔时间的下限，默认8毫秒，-1表示取消间隔
	MaxRetryBackoff    int // 每次计算重试间隔时间的上限，默认512毫秒，-1表示取消间隔
	Configs            []RServerConfig
}

type RServerConfig struct {
	Name     string
	Ip       string
	Password string
	Db       int
}

var pools map[string]*redis.Client
var redisDefaultDb string
var ctx = context.Background()

func GetCtx() context.Context {
	return ctx
}

func getConn(db string) *redis.Client {
	pool, ok := pools[db]
	if !ok {
		pool = pools[redisDefaultDb]
	}
	_, err := pool.Ping(ctx).Result()
	if err != nil {
		fmt.Println("redis kill===" + err.Error())
	}
	return pool
}

func releaseConn(conn *redis.Client) {
	if conn != nil {
		//_ = conn.Close()
	}
}

func initRedis(redisConfig RConfig) {
	pools = make(map[string]*redis.Client)
	var i int
	var configs = redisConfig.Configs
	for i = 0; i < len(configs); i++ {
		config := configs[i]
		pool := initDb(config, redisConfig)
		_, err := pool.Ping(ctx).Result()
		if err != nil {
			fmt.Printf("连接redis出错，错误信息：%v", err)
		}
		pools[config.Name] = pool
		if i == 0 {
			redisDefaultDb = config.Name
		}
	}
}

func initDb(config RServerConfig, redisConfig RConfig) *redis.Client {
	redisConfig = fillDefaultValues(redisConfig)
	return redis.NewClient(&redis.Options{
		// 连接信息
		Network:  "tcp",           // 网络类型，tcp or unix，默认tcp
		Addr:     config.Ip,       // 主机名+冒号+端口，默认localhost:6379
		Password: config.Password, // 密码
		DB:       config.Db,       // redis数据库index
		// 连接池容量及闲置连接数量
		PoolSize:     redisConfig.MaxActive,
		MinIdleConns: redisConfig.MinIdle,
		// 超时
		DialTimeout:  time.Duration(redisConfig.DialTimeout) * time.Second,
		ReadTimeout:  time.Duration(redisConfig.ReadTimeout) * time.Second,
		WriteTimeout: time.Duration(redisConfig.WriteTimeout) * time.Second,
		PoolTimeout:  time.Duration(redisConfig.PoolTimeout) * time.Second,
		// 闲置连接检查包括IdleTimeout，MaxConnAge
		IdleCheckFrequency: time.Duration(redisConfig.IdleCheckFrequency) * time.Second,
		IdleTimeout:        time.Duration(redisConfig.IdleTimeout) * time.Minute,
		MaxConnAge:         time.Duration(redisConfig.MaxConnAge) * time.Second,
		// 命令执行失败时的重试策略
		MaxRetries:      redisConfig.MaxRetries,
		MinRetryBackoff: time.Duration(redisConfig.MinRetryBackoff) * time.Millisecond,
		MaxRetryBackoff: time.Duration(redisConfig.MaxRetryBackoff) * time.Millisecond,
		// 可自定义连接函数
		// 钩子函数
		OnConnect: func(ctx context.Context, cn *redis.Conn) error {
			// 仅当客户端执行命令时需要从连接池获取连接时，如果连接池需要新建连接时则会调用此钩子函数
			fmt.Printf("redis conn=%v\n", cn)
			return nil
		},
	})
}

func fillDefaultValues(redisConfig RConfig) RConfig {
	// 连接池容量及闲置连接数量
	if redisConfig.MinIdle <= 0 {
		redisConfig.MinIdle = 1 // 连接池最大socket连接数，默认为4倍CPU数， 4 * runtime.NumCPU
	}
	if redisConfig.MaxActive <= 0 {
		redisConfig.MaxActive = 16 // 在启动阶段创建指定数量的Idle连接，并长期维持idle状态的连接数不少于指定数量；。
	}
	// 超时
	if redisConfig.DialTimeout <= 0 {
		redisConfig.DialTimeout = 5 // 连接建立超时时间，默认5秒。
	}
	if redisConfig.ReadTimeout <= 0 {
		redisConfig.ReadTimeout = 3 // 读超时，默认3秒， -1表示取消读超时
	}
	if redisConfig.WriteTimeout <= 0 {
		redisConfig.WriteTimeout = 3 // 写超时，默认等于读超时
	}
	if redisConfig.PoolTimeout <= 0 {
		redisConfig.PoolTimeout = 4 // 当所有连接都处在繁忙状态时，客户端等待可用连接的最大等待时长，默认为读超时+1秒。
	}
	// 闲置连接检查包括IdleTimeout，MaxConnAge
	if redisConfig.IdleCheckFrequency <= 0 {
		redisConfig.IdleCheckFrequency = 60 // 闲置连接检查的周期，默认为1分钟，-1表示不做周期性检查，只在客户端获取连接时对闲置连接进行处理。
	}
	if redisConfig.IdleTimeout <= 0 {
		redisConfig.IdleTimeout = 5 // 闲置超时，默认5分钟，-1表示取消闲置超时检查
	}
	if redisConfig.MaxConnAge <= 0 {
		redisConfig.MaxConnAge = 0 // 连接存活时长，从创建开始计时，超过指定时长则关闭连接，默认为0，即不关闭存活时长较长的连接
	}
	// 命令执行失败时的重试策略
	if redisConfig.MaxRetries <= 0 {
		redisConfig.MaxRetries = 0 // 命令执行失败时，最多重试多少次，默认为0即不重试
	}
	if redisConfig.MinRetryBackoff <= 0 {
		redisConfig.MinRetryBackoff = 8 // 每次计算重试间隔时间的下限，默认8毫秒，-1表示取消间隔
	}
	if redisConfig.MaxRetryBackoff <= 0 {
		redisConfig.MaxRetryBackoff = 512 // 每次计算重试间隔时间的上限，默认512毫秒，-1表示取消间隔
	}
	return redisConfig
}
